class Dep{
    constructor() {
        this.events = []
    }
    addWatcher(watcher) {
        this.events.push(watcher);
    }
    targetWatcher() {
        this.events.forEach(watch => {
            watch.touchCbk()
        })
    }
}

Dep.target = null;

let dep = new Dep();

// 给传进来的每一个对象的每一级都添加一个数据劫持
class Observer{
    constructor(data) {
        if (typeof data !== 'object') {
            return;
        }
        this.$data = data;
        this.init()
    }
    init() {
        Object.keys(this.$data).forEach(key => {
            this.observe(this.$data, key, this.$data[key])
        })
    }
    observe(target, key, value) {
        // 为的很好的遍历每一级
        new Observer(value)
        Object.defineProperty(target, key, {
            get() {
                if (Dep.target) {
                    dep.addWatcher(Dep.target)
                }
                return value
            },
            set(newValue) {
                value = newValue
                // 防止基本类型改为引用类型的情况，这样可以把改之后的应用类型的每一级也添加数据劫持
                new Observer(value);
                dep.targetWatcher()
            }
        })
    }
}



const Utils = {
    setValue(data, key, node, contentValue = 'value') {
        node[contentValue] = this.getValue(data, key)
    },
    getValue(data, key) {
        // 去处理有点的情况
        if (key.indexOf('.') > -1) {
            // 将所有的涉及到的key分解开以供使用
            let keys = key.split('.');
            // keys === [obj, age, c]
            keys.forEach(k => data = data[k])
            return data
        } else {
            return data[key]
        }
    },
    // 通过该方法修改对应的属性值
    changeValue(data, key, newValue) {
        if (key.indexOf('.') > -1) {
            let keys = key.split('.');
            for(let i = 0; i < keys.length - 1; i++) {
                data = data[keys[i]]
            }
            data[keys[keys.length - 1]] = newValue
        } else {
            data[key] = newValue
        }
    }
}








// 实现节点的监听，监听对应的属性变化
class Watcher{
    constructor(data, key, cbk) {
        Dep.target = this;
        this.cbk = cbk;
        this.data = data;
        this.key = key;
        this.init()
    }
    init() {
        let res = Utils.getValue(this.data, this.key);
        Dep.target = null;
        return res
    }
    touchCbk() {
        let res = this.init()
        this.cbk(res)
    }
}


// vue类实现
class Vue{
    constructor({el, data = {age: 30}}) {
        this.$el = document.getElementById(el);
        this.$data = data;
        // 将data中的每一项属性挂载到this上
        this.init();
        // 初始化页面
        this.initDom()
    }
    initDom() {
        // 创建一个文档碎片节点
        let fragument = document.createDocumentFragment();
        let firstChild;
        while(firstChild = this.$el.firstChild) {
            fragument.appendChild(firstChild);
        }
        // 操作DOM 赋对应的默认值
        this.complier(fragument)
        this.$el.appendChild(fragument)
    }
    complier(node) {
        // 元素节点的判断条件
        if (node.nodeType === 1) {
            let isInp = [...node.attributes].filter(item => item.nodeName.indexOf('v-') > -1)
            // 满足该条件证明节点为input
            if (isInp.length > 0) {
                node.addEventListener('input', e => {
                    console.log(e.target.value);
                    Utils.changeValue(this.$data, isInp[0].nodeValue, e.target.value)
                })
                Utils.setValue(this.$data, isInp[0].nodeValue, node)
            }
        } else if (node.nodeType === 3) {
            let contentValue = null;
            node.textContent.indexOf('{{') > -1 && (contentValue = node.textContent.split('{{')[1].split('}}')[0]);
            contentValue && Utils.setValue(this.$data, contentValue, node, 'textContent');
            contentValue && new Watcher(this.$data, contentValue, (res) => {
                node.textContent = res
            })
        }
        if (node.childNodes && node.childNodes.length > 0) {
            node.childNodes.forEach(itemNode => {
                this.complier(itemNode)
            })
        }
    }
    init() {
        Object.keys(this.$data).forEach(key => {
            // 这个地方在挂载到this上的时候需要给挂载的属性添加劫持（get, set）
            // this[key] = this.$data[key]
            this.observe(this, key, this.$data[key])
        })
        new Observer(this.$data)
    }
    observe(target, key, value) {
        Object.defineProperty(target, key, {
            get() {
                return value
            },
            set(newValue) {
                value = newValue
            }
        })
    }
}